<?php

namespace WpOrg\Requests\Tests\Response\Headers;

use WpOrg\Requests\Exception;
use WpOrg\Requests\Response\Headers;
use WpOrg\Requests\Tests\TestCase;

/**
 * @coversDefaultClass \WpOrg\Requests\Response\Headers
 */
final class ArrayAccessTest extends TestCase {

	/**
	 * Test receiving an Exception when no key is provided when setting an entry.
	 *
	 * @covers ::offsetSet
	 *
	 * @return void
	 */
	public function testOffsetSetInvalidKey() {
		$this->expectException(Exception::class);
		$this->expectExceptionMessage('Object is a dictionary, not a list');

		$headers   = new Headers();
		$headers[] = 'text/plain';
	}

	/**
	 * Test array access for the object is supported and supported in a case-insensitive manner.
	 *
	 * @covers ::offsetSet
	 * @covers ::offsetGet
	 *
	 * @dataProvider dataCaseInsensitiveArrayAccess
	 *
	 * @param string $key Key to request.
	 *
	 * @return void
	 */
	public function testCaseInsensitiveArrayAccess($key) {
		$headers                 = new Headers();
		$headers['Content-Type'] = 'text/plain';

		$this->assertSame('text/plain', $headers[$key]);
	}

	/**
	 * Data provider.
	 *
	 * @return array
	 */
	public static function dataCaseInsensitiveArrayAccess() {
		return [
			'access using case as set' => ['Content-Type'],
			'access using lowercase'   => ['content-type'],
			'access using uppercase'   => ['CONTENT-TYPE'],
		];
	}

	/**
	 * Test that when multiple headers are set using the same key, requesting the key will return the
	 * combined values flattened into a single, comma-separated string.
	 *
	 * @covers ::offsetSet
	 * @covers ::offsetGet
	 * @covers ::flatten
	 *
	 * @return void
	 */
	public function testMultipleHeaders() {
		$headers           = new Headers();
		$headers['Accept'] = 'text/html;q=1.0';
		$headers['Accept'] = '*/*;q=0.1';

		$this->assertSame('text/html;q=1.0,*/*;q=0.1', $headers['Accept']);
	}

	/**
	 * Test that non-string array keys are handled correctly.
	 *
	 * @covers ::offsetSet
	 *
	 * @dataProvider dataOffsetSetDoesNotTryToLowercaseNonStringKeys
	 *
	 * @param mixed           $key         Key to set.
	 * @param string|int|null $request_key Key to retrieve if different.
	 *
	 * @return void
	 */
	public function testOffsetSetDoesNotTryToLowercaseNonStringKeys($key, $request_key = null) {
		$headers       = new Headers();
		$headers[$key] = 'value';

		if (!isset($request_key)) {
			$request_key = $key;
		}

		$this->assertSame('value', $headers[$request_key]);
	}

	/**
	 * Data provider.
	 *
	 * @return array
	 */
	public static function dataOffsetSetDoesNotTryToLowercaseNonStringKeys() {
		return [
			'integer key'       => [10],
			'boolean false key' => [false, 0],
		];
	}

	/**
	 * Test that multiple headers can be registered on a non-string key.
	 *
	 * @covers ::offsetGet
	 * @covers ::offsetSet
	 *
	 * @return void
	 */
	public function testOffsetSetRegisterMultipleHeadersOnIntegerKey() {
		$headers     = new Headers();
		$headers[10] = 'value1';
		$headers[10] = 'value2';

		$this->assertSame('value1,value2', $headers[10]);
	}

	/**
	 * Test that null is returned when a non-registered header is requested.
	 *
	 * @covers ::offsetGet
	 *
	 * @dataProvider dataOffsetGetReturnsNullForNonRegisteredHeader
	 *
	 * @param mixed $key Key to request.
	 *
	 * @return void
	 */
	public function testOffsetGetReturnsNullForNonRegisteredHeader($key) {
		$headers                 = new Headers();
		$headers['Content-Type'] = 'text/plain';

		$this->assertNull($headers[$key]);
	}

	/**
	 * Data provider.
	 *
	 * @return array
	 */
	public static function dataOffsetGetReturnsNullForNonRegisteredHeader() {
		return [
			// This test case also tests that no "passing null to non-nullable" deprecation is thrown in PHP 8.1.
			'null'                       => [null],
			'non-registered integer key' => [10],
			'non-registred string key'   => ['not-content-type'],
		];
	}
}
